/* CsJni - .NET / Java Bridge
 * 
 * Copyright 2010 Robert E. Burke Licensed under the Apache License, Version 
 * 2.0 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 
 * law or agreed to in writing, software distributed under the License is 
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 * KIND, either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License. 
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace CsJni
{
    /*
     * Depending on the version of Java AND whether this .NET process is a console or 
     * windows process, you may or may not get stdout/stderr to the debug or console 
     * window.  This class allows you to always get stdout/stderr if desired.
     */
    [JavaToNativeCounterpart("csjni.RedirectOutputStream")]
    internal sealed class JavaStdStreamRedirector
    {
        private JavaStdStreamRedirector() { }

        internal static void Init(JniEnvironment environment)
        {
            if (JavaVM.IsLogTypeEnabled(JavaVMLogTypes.RedirectStdOut | JavaVMLogTypes.RedirectStdErr))
            {
                JniLocalFrame frame = environment.CreateLocalFrame(1);
                try
                {
                    JniClass peer = JavaNativeMethodRegistrar.RegisterCounterpartAndReturnPeer(environment, 
                        typeof(JavaStdStreamRedirector));

                    if (JavaVM.IsLogTypeEnabled(JavaVMLogTypes.RedirectStdOut))
                    {
                        HookStream(environment, peer, JavaVMLogTypes.RedirectStdOut, "redirectStdOut");
                    }

                    if (JavaVM.IsLogTypeEnabled(JavaVMLogTypes.RedirectStdErr))
                    {
                        HookStream(environment, peer, JavaVMLogTypes.RedirectStdErr, "redirectStdErr");
                    }
                }
                finally
                {
                    frame.SafePop();
                }
            }
        }

        private static void HookStream(JniEnvironment environment, JniClass nativeClazz, 
            JavaVMLogTypes hook, string javaHookMethod)
        {
            JniMethod outMid = nativeClazz.GetStaticMethodId(environment, javaHookMethod, "(I)V");
            JValue[] paramArray = new JValue[1];
            paramArray[0].AssignIntValue((int)hook);
            outMid.CallStaticVoidMethod(environment, paramArray);
        }

        [UnmanagedFunctionPointer(JavaVM.CC)]
        private delegate void RedirectStdStream_Handler(/* JNIEnv */ IntPtr environmentHandle,
            /* jobject */ IntPtr selfHandle, int streamCookie, /* jstring */ IntPtr messageHandle);

        // The selfHandle parameter is part of JNI signature.
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "selfHandle")]
        [NativeMethodInfo("redirectStdStream", "(ILjava/lang/String;)V", typeof(RedirectStdStream_Handler))]
        private static void RedirectStdStream(/* JNIEnv */ IntPtr environmentHandle, /* jobject */ IntPtr selfHandle,
            int streamCookie, /* jstring */ IntPtr messageHandle)
        {
            JavaVMLogTypes hookType = (JavaVMLogTypes)streamCookie;
            if (!(hookType == JavaVMLogTypes.RedirectStdOut || hookType == JavaVMLogTypes.RedirectStdErr))
            {
                return;
            }
            if (JavaVM.IsLogTypeEnabled(hookType))
            {
                JniEnvironment environment = JavaVM.EnvironmentFromHandle(environmentHandle);
                string message = JniString.ExtractStringFromHandle(environment, messageHandle);
                if (message.Length > 0)
                { 
                    JavaVM.HandleLogging(hookType, message);
                }
            }
        }
    }
}
